using System;
using System.IO;
using System.Text;

namespace Toenda.Foundation.Audio {
	/// <summary>
	/// Reads/writes Id3v1 tags
	/// </summary>
	public class ID3v1 {
		private string _filename;
		private string _title;
		private string _artist;
		private string _album;
		private string _year;
		private string _comment;
		private int _genreId;
		private int _track;
		private bool _hasTag;

		/// <summary>
		/// Initializes a new instance of the <see cref="ID3v1"/> class.
		/// </summary>
		public ID3v1() {
			this._Init();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ID3v1"/> class.
		/// </summary>
		/// <param name="filename">The filename.</param>
		public ID3v1(string filename) {
			this._Init();

			this._filename = filename;
		}

		/// <summary>
		/// Gets or sets the filename.
		/// </summary>
		/// <value>The filename.</value>
		public string Filename {
			get { return this._filename; }
			set { this._filename = value; }
		}

		/// <summary>
		/// Gets or sets the title.
		/// </summary>
		/// <value>The title.</value>
		public string Title {
			get { return this._title; }
			set { this._title = value; }
		}

		/// <summary>
		/// Gets or sets the artist.
		/// </summary>
		/// <value>The artist.</value>
		public string Artist {
			get { return this._artist; }
			set { this._artist = value; }
		}

		/// <summary>
		/// Gets or sets the album.
		/// </summary>
		/// <value>The album.</value>
		public string Album {
			get { return this._album; }
			set { this._album = value; }
		}

		/// <summary>
		/// Gets or sets the year.
		/// </summary>
		/// <value>The year.</value>
		public string Year {
			get { return this._year; }
			set { this._year = value; }
		}

		/// <summary>
		/// Gets or sets the comment.
		/// </summary>
		/// <value>The comment.</value>
		public string Comment {
			get { return this._comment; }
			set { this._comment = value; }
		}

		/// <summary>
		/// Gets or sets the genre id.
		/// </summary>
		/// <value>The genre id.</value>
		public int GenreId {
			get { return this._genreId; }
			set { this._genreId = value; }
		}

		/// <summary>
		/// Gets the genre.
		/// </summary>
		/// <value>The genre.</value>
		public string Genre {
			get { return ID3v1Genre.GetGenreName("de", this._genreId); }
		}

		/// <summary>
		/// Gets or sets the track.
		/// </summary>
		/// <value>The track.</value>
		public int Track {
			get { return this._track; }
			set { this._track = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether this instance has tag.
		/// </summary>
		/// <value><c>true</c> if this instance has tag; otherwise, <c>false</c>.</value>
		public bool HasTag {
			get { return this._hasTag; }
			set { this._hasTag = value; }
		}

		/// <summary>
		/// Inits this instance.
		/// </summary>
		private void _Init() {
			this._hasTag = false;
			this._filename = "";
			this._title = "";
			this._artist = "";
			this._album = "";
			this._year = "";
			this._comment = "";

			this._genreId = 0;
			this._track = 0;
		}

		/// <summary>
		/// Reads this instance.
		/// </summary>
		public void Read() {
			// Read the 128 byte ID3 tag into a byte array
			FileStream oFileStream;
			oFileStream = new FileStream(this._filename, FileMode.Open);
			byte[] bBuffer = new byte[128];
			oFileStream.Seek(-128, SeekOrigin.End);
			oFileStream.Read(bBuffer, 0, 128);
			oFileStream.Close();

			// Convert the Byte Array to a String
			Encoding instEncoding = new ASCIIEncoding();   // NB: Encoding is an Abstract class
			string id3Tag = instEncoding.GetString(bBuffer);

			// If there is an attched ID3 v1.x TAG then read it 
			if(id3Tag.Substring(0, 3) == "TAG") {
				this._title = id3Tag.Substring(3, 30).Trim();
				this._artist = id3Tag.Substring(33, 30).Trim();
				this._album = id3Tag.Substring(63, 30).Trim();
				this._year = id3Tag.Substring(93, 4).Trim();
				this._comment = id3Tag.Substring(97, 28).Trim();

				// Get the track number if TAG conforms to ID3 v1.1
				if(id3Tag[125] == 0) {
					this._track = bBuffer[126];
				}
				else {
					this._track = 0;
				}

				this._genreId = bBuffer[127];

				this._hasTag = true;
				// ********* IF USED IN ANGER: ENSURE to test for non-numeric year
			}
			else {
				this._hasTag = false;
			}
		}

		/// <summary>
		/// Updates the M p3 tag.
		/// </summary>
		public void UpdateMP3Tag() {
			// Trim any whitespace
			this._title = this._title.Trim();
			this._artist = this._artist.Trim();
			this._album = this._album.Trim();
			this._year = this._year.Trim();
			this._comment = this._comment.Trim();

			// Ensure all properties are correct size
			if(this._title.Length > 30) {
				this._title = this._title.Substring(0, 30);
			}

			if(this._artist.Length > 30) {
				this._artist = this._artist.Substring(0, 30);
			}

			if(this._album.Length > 30) {
				this._album = this._album.Substring(0, 30);
			}

			if(this._year.Length > 4) {
				this._year = this._year.Substring(0, 4);
			}

			if(this._comment.Length > 28) {
				this._comment = this._comment.Substring(0, 28);
			}

			// Build a new ID3 Tag (128 Bytes)
			byte[] tagByteArray = new byte[128];
			for(int i = 0; i < tagByteArray.Length; i++) {
				tagByteArray[i] = 0; // Initialise array to nulls
			}

			// Convert the Byte Array to a String
			// NB: Encoding is an Abstract class
			// ************ To DO: Make a shared instance of ASCIIEncoding so we don't keep creating/destroying it
			Encoding instEncoding = new ASCIIEncoding();

			// Copy "TAG" to Array
			byte[] workingByteArray = instEncoding.GetBytes("TAG");
			Array.Copy(workingByteArray, 0, tagByteArray, 0, workingByteArray.Length);
			
			// Copy Title to Array
			workingByteArray = instEncoding.GetBytes(this._title);
			Array.Copy(workingByteArray, 0, tagByteArray, 3, workingByteArray.Length);
			
			// Copy Artist to Array
			workingByteArray = instEncoding.GetBytes(this._artist);
			Array.Copy(workingByteArray, 0, tagByteArray, 33, workingByteArray.Length);
			
			// Copy Album to Array
			workingByteArray = instEncoding.GetBytes(this._album);
			Array.Copy(workingByteArray, 0, tagByteArray, 63, workingByteArray.Length);
			
			// Copy Year to Array
			workingByteArray = instEncoding.GetBytes(this._year);
			Array.Copy(workingByteArray, 0, tagByteArray, 93, workingByteArray.Length);
			
			// Copy Comment to Array
			workingByteArray = instEncoding.GetBytes(this._comment);
			Array.Copy(workingByteArray, 0, tagByteArray, 97, workingByteArray.Length);
			
			// Copy Track and Genre to Array
			tagByteArray[126] = System.Convert.ToByte(this._track);
			tagByteArray[127] = System.Convert.ToByte(this._genreId);

			// SAVE TO DISK: Replace the final 128 Bytes with our new ID3 tag
			FileStream oFileStream = new FileStream(this._filename, FileMode.Open);

			if(this._hasTag) {
				oFileStream.Seek(-128, SeekOrigin.End);
			}
			else {
				oFileStream.Seek(0, SeekOrigin.End);
			}

			oFileStream.Write(tagByteArray, 0, 128);
			oFileStream.Close();

			this._hasTag = true;
		}
	}
}
